<?php
namespace Services;

/**
 * File holds class for business data manipulation
 *
 * PHP version 5.3.5
 *
 * @category   CM
 * @package    Services
 * @subpackage -
 * @author     markus karileet <markuskarileet@hotmail.com>
 * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL
 * @link       -
 */

class Business extends ORM {
	/**
	 * Method searches DB for business with code and returns it when found
	 * @param var $code Business code
	 * @return \Entities\Business on success
	 * @throws \Exception when select fails
	 */
	public function getBusinessByCode($code) {
		$business = new \Entities\Business();
		$business->setCode($code);
		return $this->select($business);
	}
	
	/**
	 * Method returns businesses table talker stuff
	 * @param \Entities\Business $business
	 * @return \Entities\TableTalker on success
	 * @throws \Exception when select fails
	 */
	public function getTableTalker(\Entities\Business $business) {
		$template = new \Entities\TableTalker();
		$template->setBusiness($business->getId());
		return $this->select($template);
	}
	
	/**
	 * Method returns all companies
	 * @return array Array of \Entities\Business on success
	 * @throws \Exception when select fails
	 */
	public function getAll() {
		$businesses = false;
		$template = new \Entities\Business();
		$businesses = $this->selectMulti($template);
		return $businesses;
	}
	
	/**
	 * Method inserts new business by code. Theese businesses need to be updated
	 * and a correct name should be entered as well as other params
	 * @param var $code Business code to insert
	 * @return \Entities\Business 
	 * @throws \Exception when insertion fails
	 */
	public function insertTemplate($code) {
		$business = new \Entities\Business();
		$business->setCode($code);
		$business->setName(ucfirst($code));
		$business->setDescription('TODO!');//TODO: autogenerate code
		$id = $this->insert($business);
		$business->setId($id);
		return $business;
	}
	
	/**
	 * Method calculates Net Promoter Score
	 * TODO: move to feedback service
	 * @param array $ratings Array of Feedback objects
	 * @return float NPS score
	 */
	public function calculateNps(array $ratings) {
		$totalNumber = count($ratings);
		$detractors = 0;
		$promoters = 0;
		foreach (array_keys($ratings) as $itr) {
			//read total number of promoters / detractors to get NPS
			if ($ratings[$itr]->getRating() >= \Enum\NPS::Promoters) {
				$promoters++;
			} else if ($ratings[$itr]->getRating() < \Enum\NPS::Passive
                && $ratings[$itr]->getRating() != null) {
				$detractors++;
			} else if ($ratings[$itr]->getRating() == null) {
			    //get total numbers of feedbacks that have a rating
			    $totalNumber--;
			}
		}
	
		//calculate percentages for NPS calculation
		if ($totalNumber != 0) {
    		$percentageOfPromoters = $promoters * 100 / $totalNumber;
    		$percentageOfDetractors = $detractors * 100 / $totalNumber;
		} else {
		    $percentageOfPromoters = 0;
		    $percentageOfDetractors = 0;
		}
		// return NPS = percentage of promoters - percentage of detractors
		return $percentageOfPromoters - $percentageOfDetractors;
	}
	
	/**
	 * Method reads NPSes from input businesses and orders scores from best to 
	 * worst
	 * TODO:move to feedback service
	 * @param \Entities\Business[] $businesses
	 * @return array Array of NPSes array('business id' => NPS) 
	 * @throws \Exception
	 */
	public function getNpsScores($businesses) {
		$fb = new \Entities\Feedback();
		$feedbackService = new \Services\Feedback();
		$npsArray = array();
		if (is_array($businesses)) {
			foreach (array_keys($businesses) as $key) {
				//read ratings and generate NPS
				$fb->setBusiness($businesses[$key]->getId());
				$ratings = $feedbackService->selectMulti($fb);
				//calculate NPS only if feedback was found
				if ($ratings) {
					$nps = $this->calculateNps($ratings);
				} else {
					$nps = 0;
				}
				$npsArray[$key] = $nps;
			}
		}
		//sort from best to worse
		arsort($npsArray);
		return $npsArray;
	}
	
	/**
	 * Method updates business.
	 * @param array $post HTTP POST
	 * @return boolean
	 * @throws \Exception
	 */
	public function prepareUpdate($post) {
		$out = false;
		if (isset($post) && count($post) > 0 && @$post['business'] > 0) {
			$template = new \Entities\Business();
			$template->setId($post['business']);
			$business = $this->select($template);
			$business->setName(\Misc\Tools::cleanInput($post['compname']));
			$business->setAccount(\Misc\Tools::cleanInput($post['account']));
			$business->setDescription(\Misc\Tools::cleanInput($post['description']));
			$business->setAddress(\Misc\Tools::cleanInput($post['address']));
			$business->setPhone(\Misc\Tools::cleanInput($post['phone']));
			$business->setWebsite(\Misc\Tools::cleanInput($post['website']));
			$business->setPublicComments($post['publicComments']);
			$out = $this->update($business);
		}
		return $out;
	}
	
	/**
	 * Method inserts business
	 * @param array $post HTTP POST
	 * @return \Entities\Business inserted business
	 * @throws \Exception
	 */
	public function prepareInsert($post) {
		$template = false;
		$accountService = new \Services\Account();
		if(isset($post) && count($post) > 0) {
			//check for user credentials
			$user = $accountService->checkToken();
			if ($user) {
				$template = new \Entities\Business();
				$template->setName(\Misc\Tools::cleanInput($post['businessname']));
				$template->setDescription(\Misc\Tools::cleanInput($post['description']));
				$template->setAccount($user->getId());
				$template->setType($post['category']);
				$template->setCode($this->generateCode($template->getName()));
				$template->setAddress(\Misc\Tools::cleanInput($post['address']));
				$template->setPhone(\Misc\Tools::cleanInput($post['phone']));
				$template->setWebsite(\Misc\Tools::cleanInput($post['website']));
				$id = $this->insert($template);
				$template->setId($id);
			}
		}
		return $template;
	}
	
	/**
	 * Method notifies manager in a desired way: by email or sms. If notification
	 * option is not chosen or e-mail is not set then no notification is sent 
	 * @param \Entities\Feedback $feedback
	 * @return boolean True on success, false on error or corrupt notification 
	 * flag
	 * TODO: This into account of feedback class!
	 * @throws \Exception
	 */
	public function notifyManager(\Entities\Feedback $feedback) {
		$retval = false;
		$business = new \Entities\Business();
		$business->setId($feedback->getBusiness());
		$business = $this->select($business);
		$userId = $feedback->getUser();
		$user = '';
		if ($userId > 0) {
		    $userObj = new \Entities\User();
		    $userObj->setId($userId);
		    $user = $this->select($userObj);
		}
		$account = new \Entities\Account();
		$account->setId($business->getAccount());
		
		if ($account = $this->select($account)) {
			if (strlen($account->getEmail()) > 0) {
				switch ($account->getNotification()) {
					case \Enum\Notification::Email:
						$to = $account->getEmail();
						if ($feedback->getRating() != null) {
						    $subject = sprintf('%s sai hinde %d!', $business->getName(), $feedback->getRating());
						} else {
						    $subject = sprintf('%s sai kommentaari!', $business->getName());
						}
						$body = sprintf(
							'<p><b>%s</b> <br>%s</p><p>%s</p><p><a href="%s">Link tagasisidele</a></p>',
							date('d.m.Y H:i', $feedback->getTime()),
							nl2br(stripslashes($feedback->getComment())),
							$user,
							WWW . WEBROOT . '/companyDetails/'.$business->getId() . '#feedback_'.$feedback->getId()
						);
						$altBody = sprintf(
							'%s: %s %s %s',
							date('d.m.Y H:i', $feedback->getTime()),
							stripslashes($feedback->getComment()),
							$user,
							WWW . WEBROOT . '/companyDetails/'.$business->getId() . '#feedback_'.$feedback->getId()
						);
						$retval = \Misc\Tools::sendEmail($to, $subject, $body, $altBody);
						break;
					case \Enum\Notification::Sms:
						$retval = true;
						break;
					default:
						break;
				}
			}
		}
		return $retval;
	}
	
	/**
	 * Method retrieves businesses by account
	 * @param \Entities\Account $account User account whos businesses to retrieve
	 * @return array Account-related businesses
	 * @throws \Exception when select fails or account object does not have an Id
	 */
	function getBusinessByAccount(\Entities\Account $account) {
	    $businesses = false;
	    if ($account->getId() > 0) {
    	    $object = new \Entities\Business();
    	    $object->setAccount($account->getId());
    	    $businesses = $this->selectMulti($object);
	    } else {
	        throw new \Exception('Account does not have an ID!');
	    }
	    return $businesses;
	}
	
	/**
	 * Method generates code from business name. Try to avoid generics in name 
	 * such as restoran
	 * @param unknown_type $name
	 */
	public function generateCode($name) {
		$generics = array('restoran', 'kohvik', 'cafe', 'restaurant');
		$out = '';
		//strip non alphanumeric chars
		$name = preg_replace("/[^A-Za-z0-9 ]/", '', $name);
		$parts = explode(' ', $name);
		foreach ($parts as $part) {
			if (!in_array(strtolower($part), $generics)) {
				$out = strtoupper($part);
				break;
			}
		}
		$i = 0;
		//check if number is needed
		do {
			$i++;
			$business = new \Entities\Business();
			$business->setCode($out.$i);
			$business = $this->select($business);
		} while ($business);
		
		return $out.$i;
	}
}